package tleng_tp;

import java.util.Set;
import java.util.TreeSet;

public final class Magnitude{
	private boolean hasError;
	
	private String errorMessage;
	
	private float magnitudeValue;
	
	private Set<Unit> units;
	
	public boolean getErrorStatus(){ return hasError; }
	
	public String getErrorMessage(){ return errorMessage; }
	
	public Set<Unit> getUnits(){
		Set<Unit> retUnits = new TreeSet<Unit>();
		for(Unit unit:units)
			retUnits.add(new Unit(unit.getName(), unit.getPower()));
		return retUnits;
	}
	
	public Magnitude(int value){
		this((float)value);		
	}
	
	public Magnitude(int value, Unit unit){
		this((float)value, unit);
	}
	
	public Magnitude(float value){
		this(value, null);
	}
	
	public Magnitude(float value, Unit unit){
		hasError = false;
		errorMessage = "";
		magnitudeValue = value;
		units = new TreeSet<Unit>();
		if(unit != null)
			units.add(new Unit(unit.getName(), unit.getPower()));
	}
	
	public Magnitude(Unit unit){
		this(1, unit);
	}	
	
	
	public Magnitude add(Magnitude other){
		Magnitude retValue = (Magnitude)clone();		
		if(retValue.CheckSameUnits(other)){
			retValue.magnitudeValue += other.magnitudeValue;
		}
		return retValue;
	}
	
	public Magnitude substract(Magnitude other){
		Magnitude retValue = (Magnitude)clone();		
		if(retValue.CheckSameUnits(other)){
			retValue.magnitudeValue -= other.magnitudeValue;
		}
		return retValue;
	}
	
	public Magnitude multiply(Magnitude other){
		Magnitude retValue = (Magnitude)clone();	
		retValue.MergeUnits(other, true);
		retValue.magnitudeValue *= other.magnitudeValue;
		return retValue;
	}
	
	public Magnitude divide(Magnitude other){
		Magnitude retValue = (Magnitude)clone();	
		retValue.MergeUnits(other, false);
		retValue.magnitudeValue /= other.magnitudeValue;
		return retValue;
	}	
	
	public Magnitude power(int powerValue){
		Magnitude retValue = (Magnitude)clone();	
		for(Unit unit:retValue.units){
			unit.setPower(unit.getPower() * powerValue);
		}
		retValue.magnitudeValue = (float)Math.pow(retValue.magnitudeValue, powerValue);
		return retValue;		
	}
	
	public void MergeUnits(Magnitude other, boolean multiplying){
		TreeSet<Unit> newUnits = new TreeSet<Unit>();
		for(Unit unit:units){
			newUnits.add(new Unit(unit.getName(), unit.getPower()));
		}
		for(Unit otherUnit:other.getUnits()){
			for(Unit newUnit:newUnits){
				if(newUnit.getName() == otherUnit.getName()){
					newUnit.setPower(newUnit.getPower() + (multiplying? otherUnit.getPower()
							: -otherUnit.getPower()));
					break;
				}
			}
			newUnits.add(new Unit(otherUnit.getName(), multiplying? otherUnit.getPower() : -otherUnit.getPower()));
		}
		units = new TreeSet<Unit>();
		
		for(Unit newUnit:newUnits){
			if(newUnit.getPower() != 0)
				units.add(new Unit(newUnit.getName(), newUnit.getPower()));
		}
	}
	
	private boolean CheckSameUnits(Magnitude other){
		Object[] myUnits = getUnits().toArray();
		Object[] otherUnits = other.getUnits().toArray();
		
		if(myUnits.length != otherUnits.length){
			hasError = true;
			errorMessage += ("Las unidades de: '" + toString() + "' y: '" +
					other.toString() + "'son de largos distintos\n");
			return false;
		}
		for(int i = 0; i < myUnits.length; i++){
				if(!myUnits[i].equals(otherUnits[i])){
					hasError = true;
					errorMessage += ("Las unidades de: '" + toString() + "' y: '" +
							other.toString() + "'no coinciden\n");
					return false;
			}
		}
		return true;
	}
	
	public Object clone(){
		Magnitude clone = new Magnitude(magnitudeValue);
		clone.units = getUnits();
		clone.hasError = hasError;
		clone.errorMessage = errorMessage;
		return clone;
	}
	
	public String toString(){
		String unitsString = "";
		for(Unit unit:units)
			unitsString += " " + unit.toString();
		return magnitudeValue + unitsString;		
	}
}
